ex13_01

如果一个构造函数的第一个参数是自身类型的引用，且任何额外参数都有默认值，则此构造函数是拷贝构造函数；
拷贝构造函数在以下几种情况下都会被使用：
1.拷贝初始化（用=定义变量）；
2.将一个对象作为实参传递给一个非引用类型的形参；
3.用花括号列表初始化一个数组中的元素或一个聚合类中的成员
4.初始化标准库容器或者调用其insert/push操作时，容器会对其元素进行拷贝初始化


ex13_02

这个声明是非法的，因为这条声明符合使用拷贝构造函数的第二种情况--将一个对象作为实参传递给一个非引用类型的形参，因此这个拷贝构造函数的参数应该是引用类型的，但是实际上这个参数是非引用类型的，所以是错误的，应该进行如下的修改：
Sales_data::Sales_data(Sales_data &rhs)

ex13_03

StrBlob只有一个成员函数，是shared_ptr<vector<string>> data,所以拷贝StrBlob时，根据“对类类型的成员，会使用其拷贝构造函数来进行拷贝”这一规则，会使用一个拷贝构造函数来拷贝StrBlob的成员，又因为StrBlob中的成员函数是智能指针shared_ptr，对其进行拷贝会使其引用计数加1；
StrBlobPtr有两个成员函数，分别是weak_ptr<vector<string>>和普通类型的，所以根据规则，对于weak_ptr<vector<string>>类型的用拷贝构造函数来对其进行拷贝，其引用计数不变1，curr是普通类型的，直接拷贝就可以了


ex13_04

拷贝初始化通常通过拷贝构造函数来实现，因此，如果下面的程序片段满足发生拷贝初始化的条件，则该片段使用了拷贝构造函数：
1.因为在函数调用的过程中，具有非引用类型的参数要进行拷贝初始化---本段程序中，Point foo_bar(Point arg)的参数不是引用类型的，因此进行拷贝初始化，使用了拷贝构造函数；
2.从一个返回类型为非引用类型的函数返回一个对象需要用拷贝初始化---因此return *head使用了拷贝构造函数；
3.将一个对象作为实参传递给一个非引用类型的形参需要用拷贝初始化---因此Point local=arg,*head=local进行了拷贝初始化，使用了拷贝构造函数
4.用花括号列表初始化一个数组中的元素要进行拷贝初始哈---Point pa[4]={local，*heap}进行了拷贝初始化，使用了拷贝构造函数，将local、*heap拷贝到数组中前两个元素

ex13_05
拷贝构造函数的定义应该是其第一个参数是自身类类型的引用，且大部分都是const的引用，所以应该
HasPtr::HasPtr(const HasPtr &hr)
{
	ps = new string(*hr.ps);
	i = hr.i;
}

ex13_06

拷贝赋值运算符，是一个名为operator=的函数，由返回类型和参数列表组成。
当需要对类的对象进行拷贝操作时，就会使用拷贝赋值运算符。
如果一个类没有定义自己的拷贝赋值运算符，编译器就会为其合成拷贝赋值运算符，完成赋值操作，但是对于某些类，合成拷贝赋值运算符用来禁止该类型对象的赋值

ex13_07

这两个类都没有定义拷贝赋值运算符，因此编译器为它们定义了合成的拷贝赋值运算符，赋值一个StrBlob时，拷贝类中唯一的成员data，使用shared_ptr的拷贝赋值运算符来完成，因为对智能指针进行拷贝操作，其引用计数会加上1；
赋值一个StrBlobPtr时，类中的成员函数由智能指针weak_ptr和普通的成员curr，所以赋值weak_ptr时，其引用计数不变，赋值普通的成员函数curr时，直接进行内存复制

ex13_08

HasPtr&
HasPtr::operator=(const HasPtr &rhs)  //左侧运算对象是HasPtr
{
	auto newps = new string(*rhs.ps);  //拷贝指针指向的对象
	delete ps;  //销毁原string
	ps = newps; //指向新string
	i = rhs.i;  //使用内置的int赋值
	return *this;  //返回一个此对象的引用
}


ex13_9

析构函数是用来释放对象所使用资源的一种函数，当一个类没有定义析构函数的时候， 编译器会为它合成析构函数，对于某些类来说，合成析构函数用来阻止该类对象被销毁；在整个对象销毁过程中，析构函数体是作为成员销毁步骤之外的另一部分而进行的

ex13_10

这两个函数都没有定义析构函数，因此编译器会合成析构函数，
对于StrBlob，合成析构函数的空函数体执行完毕后，会销毁其成员函数data，这会调用shared_ptr的析构函数，将其引用计数减一，引用计数会变为0，会销毁共享的vector对象
对于StrBlobPtr，合成析构函数在隐含的析构阶段会销毁数据成员wptr和curr，其中wptr是智能指针weak_ptr类型，销毁wptr会调用weak_ptr的析构函数，引用计数不会发生变化，curr是内置类型，销毁它不会有任何特殊动作

ex13_11
该类的析构函数如下所示
	~HasPtr()
	{
		delete ps;
	}
	
ex13_12

调用析构函数的条件如下：
	• 变量在离开其作用域时被销毁
	• 当一个对象被销毁时，其成员被销毁
	• 容器（无论是标准库容器还是数组）被销毁时，其元素被销毁
	• 对于动态分配的对象，当对指向它的指针应用delete运算符时被销毁
	• 对于临时对象，当创建它的完整表达式结束时被销毁
所以本段代码中，
函数结束的时候，局部变量item1、item2的声明周期结束，Sales_data的析构函数被调用了2次；
在函数结束的说话，accum的声明周期结束，被销毁，Sales_data的析构函数被调用；
在函数结束时，trans的生命周期也结束了，但是trans是Sales_data的指针，并不是它指向的Sales_data生命周期结束，所以不会调用其析构函数释放对象使用的资源

ex13_14

下面的代码输出的内容是一样的，因为这段代码只是简单的使用了合成的拷贝构造函数，首先定义了a，然后把a的值拷贝给b，再将b的值拷贝给c，所以f函数的三个参数是一样的，所以输出的内容也是一样的

ex13_15

如果numbered定义了拷贝构造函数，那么输出的结果会发生改变，因为，这时候再次进行拷贝时已经不是简单的用合成拷贝构造函数进行拷贝了，而是numbered定义的拷贝构造函数在起作用，因此会改变输出结果；
但是新的输出结果不是0,1,2，而是3,4,5
因为在定义变量a时，默认构造函数起作用，将其序号设定为0，当定义b、c时，拷贝构造函数起作用，将他们的序号分别设定为1,2；
但是当f调用时，因为参数是numbered类型，且其参数是非引用类型的，所以会发生拷贝初始化，即又进行拷贝构造函数的使用，使得每一次都将形参s的序号设定为新值，输出的三次结果是递增的

ex13_16

拷贝初始化的条件是在函数调用过程中，函数的形参是非引用类型的，因此，如果f中的参数变成了const numbered&,那么将不会执行拷贝初始化，即不再触发拷贝构造函数将实参拷贝给形参，而是将传递实参的引用，因此，对每次调用，s都是指向实参的因哟红，序号就是实参的序号，会改变输出结果，结果变成了1,2,3

Ex13_19
         Employee类是需要定义自己的拷贝控制成员的，因为如果不定义拷贝控制函数，将会执行简单的拷贝操作，这样的话程序就失去了意义

Ex13_20
TextQuery、QueryResult没有定义拷贝控制成员，因此都是编译器为它们定义合成版本，
销毁：
TextQuery有两个成员，分别是file和wm，所以当删除file成员时，会将shared_ptr的引用计数减1，如果变为0，就是销毁所管理的动态vector对象。
对于wm，因为这是一个map容器，因此会调用map的析构函数，正确释放资源
      QueryResult有三个数据成员，对于这三个成员如果要销毁它们的时候，会调用string、shared_ptr和vector的析构函数，释放这些资源
拷贝：
      当拷贝一个TextQuery时，合成版本的拷贝构造函数会拷贝file和wm成员：对于shared_ptr指针指向的file来说，其引用计数会加1，对wm，会调用map的拷贝构造函数执行正确的拷贝操作
       拷贝QueryResult和拷贝TextQuery操作是类似的
赋值：
     当对TextQuery进行赋值操作的时候，会首先把原来的资源释放掉，引用计数会减去1，然后再进行赋值操作
     对QueryResult的操作是类似的

Ex13_21

我认为这两个类是不需要定义它们自己的拷贝控制成员的，因为这两个类中定义的成员都是容器类型或者智能指针类型的，都能够很好的进行资源的管理

ex13_23

因为我写的代码和书中所给的代买不谋而合，所以并没有什么差异，哈哈哈

ex13_24

如果没有定义析构函数，那么ps指针指向的内存就不会正确的释放，会造成内存的泄漏
如果没有定义拷贝构造函数，那么在拷贝HasPtr时，就仅仅是简单的复制ps的成员，使得两个HasPtr指向的是相同的string，当其中一个HasPtr修改string时，另一个string也会被改变

ex13_25

如果类的行为像一个值，那么它应该有自己的状态，副本和原来的对象是完全独立的，所以我们应该用拷贝赋值运算符和拷贝构造函数将vector<string>拷贝一份，使得两个StrBlob对象指向各自的数据，而不是简单的拷贝shared_ptr使得两个StrBlob指向同一个vector
  另外StrBlob不需要析构函数的原因是，他管理的成员是vector<string>，这个成员是由智能指针shared_ptr负责管理的，当需要销毁StrBlob的对象时，会调用智能指针的析构函数，正确的进行资源的管理，所以说就不需要StrBlob了


ex13_29

HasPtr中的两个成员分别是整形的i和指针型的ps，都是内置类型的，所以使用的swap是标准库版本的std::swap,所以不会使用HasPtr版本的swap，所以不会导致递归循环

ex13_32

类指针的类版本，使用编译器自带的swap()函数即可完成目标操作，并不需要自定义一个swap以交换指针所指向的值，所以并不会提升效果


ex13_33

save和remove函数的作用是添加/删除folder中的Message，如果将形参类型定义为Folder，那么就是一个传值调用，那么形参和实参就是两个相互独立的量，那么save和remove函数对形参Folder所做的所有操作都不会影响到实参，save和remove操作就失去了原来希望的作用，就没有意义了
  另一方面，save和remove函数将会改变Folder中的值，因此不能使用常量关键字const
  
 
ex13_35

Message包含string类型的content和set类型的folders，它们都是标准库类型，有完整的拷贝控制成员，因此如果使用Message的合成的拷贝控制成员，那么简单拷贝这两个成员也能实现正确的拷贝
但是Message拷贝时还要将Message添加到每个包含该Message的Folder中，删除时还要将Message删除，即分别需要调用addMsg和remMsg,因此，不能依赖合成的拷贝控制成员，而是需要设计自己的版本来完成这些工作

ex13_37

根据题意，那么Message定义的类成员应该是这样的：
void addFldr(Folder *f) { folders.insert(f); }  //向Folder中添加Message
void remFldr(Folder *f) { folders.erase(f); }   //从Folder中删除Message

ex13_38

当涉及到动态分配内存时,拷贝并交换是一个完成该功能的精简的方式. ，但是在Message类中,并未涉及到动态分配内存,这种方法并不会产生任何益处，同时还会因为很多指针操作让程序变得更复杂难难以实现
具体来说，如果采用拷贝并交换方式，执行的方式是这样的：
1.由于赋值运算符的参数是Message类型，因此会将实参拷贝给形参rhs，这样会触发拷贝构造函数，将实参的contents和folders拷贝给rhs，并调用add_to_Folders将rhs添加到folders的所有文件夹中
2.随后赋值运算符调用swap交换*this和rhs，首先遍历两者的folders，将它们从自己的文件夹中删除，然后调用string和set的swap交换它们的contents和folders，最后，再遍历两者新的folders，将它们分别添加到自己的新文件夹中
3.最后，赋值运算符结束，rhs被销毁，析构函数调用remove_frome_Folders将rhs从自己所有文件夹中删除
显然，虽然语义是正确的，达到了预期的目的，但是效率低下，rhs创建、销毁并两次添加、删除是毫无意义的，而采用拷贝赋值运算符的标准编写方式，形参rhs为引用类型，就能避免这些多余的操作，具有更好的性能

ex13_41

通过第4章表达式的学习我们可以知道，前置递增运算符用的是递增后之后的结果来进行运算，后置递增元素符用的是递增之前的结果来进行运算，本题中，要指向第一个空位置，如果使用前置递增运算符，那么就是用递增后的结果来进行构建，那么就会跳过一个位置，因此不能使用前置递增运算符来进行计算，而是应该使用后置递增运算符来进行计算

ex13_42

用StrVec代替vector<string>的话，程序应该进行如下的修改：
在TextQuery中，将input成员函数的定义修改为：shared_ptr<StrVec> input;
在QueryResult中，将input成员函数的定义修改为：shared_ptr<StrVec> input;
在QueryResult中，将其构造函数的第3个参数修改为shared_ptr<StrVec> v;
成员函数get_file的定义修改为 shared_ptr<StrVec> get_file(){return file}
TextQuery中构造函数初始化input成员的操作修改为
TextQuery::TextQuery(ifstream &is):input(new StrVec)

ex13_ 43

使用for_each和lambda重写后的free成员函数如下：
	inline void StrVec::free()
	{
		if(elements)
		{
			for_each(elements,first_free,[](std::string &s){alloc.destroy(&s);});
			alloc.deallocate(elements,cap-elements);
		}
	}
	可以发现使用for_each和lambda来代替for循环和destroy更好，因为这个版本不需要循环，而且语义更加明显

ex13_45

右值引用就是必须绑定到右值的引用，通过&&获得，右值引用只能绑定到一个将要销毁的对象上，可以自由的移动其资源
左值引用就是通常所说的常规引用，右值用的是对象的值，左值用的是对象的身份，左值引用不能绑定到要转换的表达式、字面常量或返回右值的表达式，右值引用可以绑定到这类表达式，但是右值引用不能绑定到一个左值上

返回左值的表达式包括返回左值引用的函数以及赋值、下标、解引用和前置递增/递减运算符，返回右值的包括返回非引用类型的函数及算术、关系、位和后置递增/递减运算符，左值的特点是有持久的状态，右值是短暂的

ex13_46

r1必须是右值引用，因为f是返回非引用类型的函数；
r2必须是左值引用，因为下标运算返回的是左值；
r3只能是左值引用，因为r1是一个变量，而变量是一个左值；
r4只能是右值引用，因为vi[0]*f()是一个算术表达式，返回右值


ex13_50

在String类中添加的打印语句如下：

String baz()
{
    String ret("world");
    return ret; // 返回值会避免拷贝
}

可以看出返回值会避免拷贝，返回的右值会避免拷贝

ex13_51

unique_ptr是不能够拷贝的，但是有一个例外，就是将要被销毁的unique_ptr是可以进行拷贝或销毁的，因此，在418页的clone函数中返回局部unique_ptr对象ret是可以的，因为这时候该函数执行的是一种特殊的拷贝操作“移动”，就是用了移动构造函数进行移动

ex13_52

在第一个赋值中，hp2是一个左值，不能用移动构造函数，rhs将使用拷贝构造函数来进行初始化，拷贝构造函数将分配一个新的string，并拷贝hp2指向的string
在第二个赋值中，调用了std::move将一个右值绑定到hp2上，在此情况下，拷贝构造函数和移动构造函数都是可行的，但是，因为实参是右值引用，移动构造函数是精确匹配的，因此移动构造函数从hp2拷贝指针，而不会分配任何内存


ex13_53

（1）在进行赋值拷贝时，先通过拷贝构造函数创建了hp2的拷贝rhs，然后再交换hp和rhs，rhs作为一个中间媒介，只是起到了将值从hp2传递给hp的作用，这是一个冗余的操作
类似的，在进行移动赋值时，先从hp2转移到rhs，再交换到hp也是冗余的，因此HasPtr的赋值运算符并不理想；
（2）拷贝赋值运算符：
HasPtr&
HasPtr::operator=(const HasPtr &rhs)  //左侧运算对象是HasPtr
{
	auto newps = new string(*rhs.ps);  //拷贝指针指向的对象
	delete ps;  //销毁原string
	ps = newps; //指向新string
	i = rhs.i;  //使用内置的int赋值
	return *this;  //返回一个此对象的引用
}

移动赋值运算符：
HasPtr& operator=(HasPtr &&rhs) noexcept
{
	cout<<"Move Assignment"<<endl;
	if(this!=&rhs)
	{
		delete ps;
		ps=rhs.ps;
		rhs.ps=nullptr;
		rhs.i=0;
	}
	return *this;
}
（3）比较移动赋值运算符和拷贝赋值运算符可以发现，拷贝赋值运算符的唯一好处是统一了拷贝和移动赋值运算，但是在性能角度，多了一次从rhs的间接传递，性能不好

ex13_54

如果为HasPtr定义了移动赋值运算符，但是没有改变拷贝并交换运算符的话，会出现匹配错误，因为可以有多个函数能够匹配运算，造成二义性错误
官方解答：会产生编译错误，因为对于hp=std::move（hp2）来说，两个运算符匹配的一样好，从而产生了二义性

ex13_56

首先，局部变量ret拷贝了被调用对象的一个副本，然后，对ret调用sorted，由于并非是函数返回语句或函数结束，因此编译器认为它是左值，仍然调用左值引用版本，产生递归循环

ex13_57

本题可以正确利用右值引用版本来完成排序，因为编译器认为Foo(*this)是一个“无主”的右值，对它调用sorted会匹配右值引用版本

ex13_58

练习13.56的写法会一直输出“左值引用版本”，直到栈溢出，程序退出；
练习13.57的写法会输出一个“左值引用版本”和一个“右值引用版本”后正确结束
